<!-- TODO(psimakov): use local YUI library -->
<link rel="stylesheet" type="text/css"
    href="http://yui.yahooapis.com/3.6.0/build/cssreset-context/cssreset-context-min.css">
<script
    src="http://yui.yahooapis.com/3.6.0/build/yui/yui.js"></script>
<link rel="stylesheet" type="text/css"
    href="/static/inputex-3.1.0/src/inputex/assets/skins/sam/inputex.css">
<script
    src="/static/inputex-3.1.0/src/loader.js" type='text/javascript'></script>
<script
    src="/static/inputex-3.1.0/lib/jsonPretty.js" type='text/javascript'></script>

<!-- TODO(psimakov): take styles out into a separate file -->
<style>
  div#oeditor-container {
    position: relative;
  }
  div.status-popup {
    width:400px;   /* adjust */
    left:50%;
    margin-left: -200px;  /* half of the width */
    background-color: #ffffB7;
    border: solid #cccca0 1px;
    border-radius: 5px;
    text-align: center;
    padding: 2px;
    z-index: 999;
  }
  .status-popup.fixed {
    position: fixed;
    top: 10px;
  }
  .status-popup.absolute {
    position: absolute;
    top: -45px;
  }
  div.status-controlbar {
    float: right;
  }
  div.status-controlbar button {
    font-weight: bold;
    text-decoration: none;
    padding: 0 0.2em;
  }
  div.status-message {
    padding: 0.2em;
  }

  div.form {
    width: 700px;
    margin: 20px;
    padding: 15px;
    display: none;
    text-align: left;
  }
  div#formContainer fieldset {
    border: none;
    border-top: solid 1px #999999;
    border-bottom: solid 1px #999999;
    margin-bottom: 16px;
  }
  div#formContainer fieldset legend span {
    color: #505050;
    font-weight: bold;
    padding: 4px;
  }
  div.split-from-main-group-parent {
    margin: 0 0 20px 70px !important;
  }

  div.inputEx-Field {
    width: 500px;
    padding-top: 0.2em;
    color: #999999;
  }
  div.inputEx-Field textarea {
    width: 496px;
    max-width: 496px;
    height: 200px;
    border: solid 1px #999999;
    padding: 4px;
    border-left: 6px solid #999999;
    white-space: pre;
    word-wrap: normal;
    overflow: scroll;
  }
  div.inputEx-StringField-wrapper input {
    margin-top: 2px;
    margin-bottom: 2px;
    padding: 2px;
    text-align: left;
    border: solid 1px #999999;
  }
  div.inputEx-Field input {
    width: 100%;
    border-left: 6px solid #999999;
  }
  div.inputEx-CheckBox input {
    width: auto;
  }
  div.inputEx-DateField {
    width: 480px;
  }

  div.inputex-form-buttonbar {
    margin: 0px;
    padding-left: 8px;
    padding-top: 18px;
    border-top: solid 1px #9999FF;
    text-align: center;
  }
  div.inputex-form-buttonbar a {
    text-decoration: none;
    padding: 4px;
    padding-left: 16px;
    padding-right: 16px;
    margin-right: 16px;
    font-weight: bold;
    color: #000000;
  }
  a.inputex-list-link {
    float: right;
  }
  a.inputEx-Button,
  div.inputEx-Field input.inputEx-Button {
    width: auto;
    background-color: #666;
    color: #FFFFFF;
    padding: 4px;
    padding-left: 16px;
    padding-right: 16px;
    margin-left: 4px;
    margin-right: 4px;
    font-weight: bold;
    text-decoration: none;
    border: none;
  }

  textarea.raw-text-editor {
    width: 580px;
    height: 300px;
  }
  div.rte-control {
    margin-right: 35px;
    float:  right;
    background-color: #666;
    color:  white;
    padding: 0 0.4em;
    border-radius: 0.2em;
    font-weight:  bold;
  }

  .pull-right {
    float: right;
  }

  /* TODO(jorr): Rework this CSS to have clearer headers for nested properties */

  div#formContainer div.inputEx-ListField fieldset {
    border: none;
    min-width: 300px;
    margin-bottom: 0;
    -webkit-padding-before: 0;
    -webkit-padding-after: 0;
  }
  div#formContainer div.inputEx-ListField div.inputEx-label {
    width: auto;
  }
  div.inputEx-ListField-subFieldEl > fieldset > div.inputEx-fieldWrapper {
    white-space: nowrap;
  }
  div.inputEx-ListField-subFieldEl > fieldset > div.inputEx-fieldWrapper > div {
    float: none;
    display: inline;
    vertical-align: top;
  }
  div.inputEx-ListField-childContainer > div {
    white-space: nowrap;
  }
  div.inputEx-ListField-Arrow {
    float: none;
    display: inline-block;
    float: right;
  }
  a.inputEx-List-link {
    display: none;
  }
  img.framed {
    border: 1px solid #ccc;
    padding: 3px;
    max-width: 400px;
  }
</style>

<div id='oeditor-container'>
  <div class='status-popup absolute' id='formStatusPopup' style='display: none;'>
    <div class='status-controlbar'>
      <button onclick='cbHideMsg()'>X</button>
    </div>
    <div class='status-message' id='formStatusMessage'></div>
  </div>
  <div style='width: 100%;' align='center' class='yui3-skin-sam yui-skin-sam'>
    <div class='form' id='formContainer'></div>
  </div>
</div>

<!-- TODO(psimakov): take most of JS out into a separate file -->
<script type='text/javascript'>
  // server communications timeout
  var ajaxRpcTimeoutMillis = 15 * 1000;
  // XSSI prefix. Must be kept in sync with models/transforms.py.
  var xssiPrefix = ")]}'\n";

  function cbShowMsg(text){
    var popup = document.getElementById("formStatusPopup");
    var message = document.getElementById("formStatusMessage");
    message.textContent = text;  // FF, Chrome
    message.innerText = text;    // IE
    popup.style.display = "block";
  }

  function cbHideMsg(){
  elem = document.getElementById("formStatusPopup");
    elem.style.display = "none";
  }

  /**
   * Compare two JS objects for equality by value.
   */
  function deepEquals(x, y) {
    if (typeof(x) != "object") {
      return x === y;
    }
    if (typeof(y) != "object" || propertyCount(x) != propertyCount(y)) {
      return false;
    }
    for (e in x) {
      if (x.hasOwnProperty(e) && (typeof(y[e]) == "undefined" || !deepEquals(x[e], y[e]))) {
        return false;
      }
    }
    return true;
  }

  /**
   * Parses JSON string that starts with an XSSI prefix.
   */
  function parseJson(s) {
    return JSON.parse(s.replace(xssiPrefix, ''));
  }

  function propertyCount(x) {
    var count = 0;
    for (e in x) {
      if (x.hasOwnProperty(e)) {
        ++count;
      }
    }
    return count;
  }

  function formatServerErrorMessage(status, message) {
    var msg = "Unknown error (" + status + ").";
    if (message) {
      msg = message;
    }
    switch (status) {
      case 412:
        return msg;
      default:
        return "Server error; error code " + status + ". " + msg;
    }
  }

  /**
   * Render an asset as an image if it is a recognized image type; otherwise
   * simply expose a link to the asset.
   */
  function renderAsset(Y, uri) {
    imageExts = ['png', 'jpg', 'jpeg', 'gif'];
    var div = document.createElement('div');
    for (i in imageExts) {
      var ext = imageExts[i];
      if (uri.length >= ext.length &&
          uri.substring(uri.length - ext.length).toLowerCase() == ext) {
        var img = document.createElement('img');
        img.setAttribute('src', uri);
        img.setAttribute('class', 'framed');
        div.appendChild(img);
        div.appendChild(document.createElement('br'));
        break;
      }
    }
    var link = document.createElement('a');
    link.setAttribute('href', uri);
    link.setAttribute('target', '_blank');
    link.appendChild(document.createTextNode(uri));
    div.appendChild(link);
    return div;
  }

  /**
   * If there is a form element marked with class 'split-from-main-group'
   * then this is pulled out of the fieldset and inserted between the fieldset
   * and the button bar.
   */
  function moveMarkedFormElementsOutOfFieldset(Y) {
    var splitFromMainGroup = Y.one('div.split-from-main-group');
    if (splitFromMainGroup != null) {
      // InputEx puts the class name on the div which contains the input element
      // but we really want to work with the parent.
      var splitFromMainGroupParent = splitFromMainGroup.get('parentNode');
      splitFromMainGroupParent.addClass('split-from-main-group-parent');
      Y.one('#cb-oeditor-form').insertBefore(splitFromMainGroupParent,
        Y.one('#cb-oeditor-form > div.inputEx-Form-buttonBar'));
    }
  }

  function keepPopupInView(Y) {
    var container = Y.one('#oeditor-container');
    var popup = Y.one('#formStatusPopup');
    // The 'absolute' style positions the popup 45px above the top of the
    // container and we want 'fixed' to pin it at 10px below the top of the
    // window, so check that the container isn't less than 55px from the top of
    // the window.
    if (container.getY() - container.get('docScrollY') <= 55) {
      popup.addClass('fixed');
      popup.removeClass('absolute');
    } else {
      popup.removeClass('fixed');
      popup.addClass('absolute');
    }
  }

  // set initial UI state
  document.getElementById("formContainer").style.display = "none";
  cbShowMsg("Loading...");

  // keep all our global objects here
  cb_global = {};

  // various URL's needed by the form
  cb_global.get_url = '{{ get_url|safe }}';
  cb_global.save_url = '{{ save_url|safe }}';
  cb_global.save_args = {{ save_args|safe }};
  cb_global.exit_button_caption = '{{ exit_button_caption|safe }}';
  cb_global.exit_url = '{{ exit_url|safe }}';
  cb_global.save_method = '{{ save_method }}';
  cb_global.auto_return = {% if auto_return %}true{% else %}false{% endif %};

  // If this value is '', deletions are not allowed
  cb_global.delete_url = '{{ delete_url|safe }}';
  cb_global.delete_method = '{{ delete_method }}';

  // button captions
  cb_global.save_button_caption = '{{ save_button_caption }}'

  // set home folder
  YUI_config.groups.inputex.base = '/static/inputex-3.1.0/src/';

  // here is the main method
  YUI({filter: "raw"}).use(
    {{ required_modules|safe }},
    function (Y) {

      // Add a new visu handler to inputEx, to look for a named function. The
      // must be a member of window and should accept Y and the value of the
      // target field as its parameters. It should return the correct inputEx
      // widget initialized to render the given data.
      if (Y.inputEx.visus) {
        Y.inputEx.visus.funcName = function(options, value) {
          return window[options.funcName](Y, value);
        }
      }

      Y.on('scroll', function(e) {
        keepPopupInView(Y);
      });

      // here is the object schema
      var schema = {
        root : {{ schema|safe }}
      };

      // inject inputex annotations
      {{ schema_annotations|safe }}

      // build form definition from the json schema
      builder = new Y.inputEx.JsonSchema.Builder({
        'schemaIdentifierMap': schema
      });
      var inputExDefinition = builder.schemaToInputEx(schema.root);

      // save button
      save_button = {type: 'submit-link', value: cb_global.save_button_caption, onClick: function() {
            cbShowMsg("Saving...");

          // record current state
          var lastSavedFormValue = cb_global.form.getValue();

          // format request
          var request_save = cb_global.save_args;
          request_save.payload = JSON.stringify(lastSavedFormValue);

          // append xsrf_token if provided
          if (cb_global.xsrf_token) {
              request_save.xsrf_token = cb_global.xsrf_token;
          }

          // format request
          request_data = {"request": JSON.stringify(request_save)};

          // async post data to the server
          var url = cb_global.save_url;

            yioConfig = {
              method: 'PUT',
              data: request_data,
              timeout : ajaxRpcTimeoutMillis,
              on: {
                  complete: function(transactionId, response, args) {
                    var json;
                    if (response && response.responseText) {
                      json = parseJson(response.responseText);
                    } else {
                      cbShowMsg("Server did not respond. Please reload the page to try again.");
                      return;
                    }

                  if (json.status != 200) {
                    cbShowMsg(formatServerErrorMessage(json.status, json.message));
                    return;
                  }

                  // save lastSavedFormValue
                  cb_global.lastSavedFormValue = lastSavedFormValue;

                    // update UI
                    cbShowMsg(json.message);
                    setTimeout(function(){
                      cbHideMsg();
                      if (cb_global.auto_return) {
                        window.location = cb_global.exit_url;
                      }
                    }, 5000);
                  }
              }
            };

            if (cb_global.save_method == 'upload') {
              yioConfig.method = 'POST';
              yioConfig.form = {
                id: 'cb-oeditor-form',
                upload: true
              };
            }

            Y.io(url, yioConfig);
            return false;
      }};

      // close button
      close_button = {type: 'link', value: cb_global.exit_button_caption, onClick:function(e) {
          if (deepEquals(cb_global.lastSavedFormValue, cb_global.form.getValue()) ||
              confirm("Abandon all changes?")) {
            window.location = cb_global.exit_url;
          }
      }};

      // delete button
      delete_button = {type: 'link', value: 'Delete',
          className: 'inputEx-Button inputEx-Button-Link pull-right',
          onClick:function(e) {
              if (confirm("Are you sure you want to delete this {{ type_label|safe }}?")) {
                  if (cb_global.delete_method == 'delete') {
                    // async delete
                    Y.io(cb_global.delete_url, {
                      method: 'DELETE',
                      timeout : ajaxRpcTimeoutMillis,
                      on: {
                        success: function(id, o, args) {
                          var json = parseJson(o.responseText);
                          if (json.status != 200) {
                            cbShowMsg(formatServerErrorMessage(json.status, json.message));
                            return;
                          } else {
                            window.location = cb_global.exit_url;
                          }
                        },
                        failure : function (x,o) {
                          cbShowMsg("Server did not respond. Please reload the page to try again.");
                        }
                      }
                    });
                  } else {
                    // form delete
                    var form = document.createElement('form');
                    form.method = cb_global.delete_method;
                    form.action = cb_global.delete_url;
                    document.body.appendChild(form);
                    form.submit();
                  }
              }
          }
      };

      // choose buttons to show
      inputExDefinition.buttons = [];
      if (cb_global.save_url && cb_global.save_method) {
        inputExDefinition.buttons.push(save_button);
      }
      inputExDefinition.buttons.push(close_button);
      if (cb_global.delete_url != '') {
        inputExDefinition.buttons.push(delete_button);
      }

      // Disable the animated highlighting of list fields on reordering
      if (Y.inputEx.ListField) {
        Y.inputEx.ListField.prototype.arrowAnimColors = {
          'from': '',
          'to': ''
        };
      }

      if (Y.inputEx.RTEField) {
        // Override the inputEx.RTEField.renderComponent method to insert
        // a control to toggle between showing the RTE and a raw text editor
        var superRteFieldRenderer = Y.inputEx.RTEField.prototype.renderComponent;
        Y.inputEx.RTEField.prototype.renderComponent = function() {
          superRteFieldRenderer.apply(this);
          var showRteText = "Rich Text";
          var hideRteText = "Plain Text";
          var Dom = Y.DOM;
          var showRteFlag = true;
          var toggle = document.createElement("div");
          var toggleText = document.createTextNode(hideRteText);
          toggle.appendChild(toggleText);
          Dom.addClass(toggle, "rte-control");
          var rteField = this;

          toggle.onclick = function() {
            showRteFlag = !showRteFlag;
            if (showRteFlag) {
              showRte(rteField);
              toggleText.nodeValue = hideRteText;
            } else {
              hideRte(rteField);
              toggleText.nodeValue = showRteText;
            }
          };
          rteField.divEl.appendChild(toggle);

          function showRte(rteField) {
            var editor = rteField.editor,
                rteDiv = editor.get('element').previousSibling,
                textArea = editor.get('element');

            if (rteField._cbGetValue) {
              rteField.getValue = rteField._cbGetValue;
            }

            Dom.setStyle(rteDiv, 'position', 'static');
            Dom.setStyle(rteDiv, 'top', '0');
            Dom.setStyle(rteDiv, 'left', '0');
            Dom.setStyle(textArea, 'visibility', 'hidden');
            Dom.setStyle(textArea, 'top', '-9999px');
            Dom.setStyle(textArea, 'left', '-9999px');
            Dom.setStyle(textArea, 'position', 'absolute');
            editor.get('element_cont').addClass('yui-editor-container');
            editor._setDesignMode('on');
            editor.setEditorHTML(editor.get('textarea').value);
          }

          function hideRte(rteField) {
            var editor = rteField.editor,
                rteDiv = editor.get('element').previousSibling,
                textArea = editor.get('element');

            editor.saveHTML();

            rteField._cbGetValue = rteField.getValue;
            rteField.getValue = function() {
              return editor.get('textarea').value;
            };

            Dom.setStyle(rteDiv, 'position', 'absolute');
            Dom.setStyle(rteDiv, 'top', '-9999px');
            Dom.setStyle(rteDiv, 'left', '-9999px');
            editor.get('element_cont').removeClass('yui-editor-container');
            Dom.setStyle(textArea, 'visibility', 'visible');
            Dom.setStyle(textArea, 'top', '');
            Dom.setStyle(textArea, 'left', '');
            Dom.setStyle(textArea, 'position', 'static');
            Dom.addClass(textArea, 'raw-text-editor');
          }
        };
      }

      // create form and bind it to DOM
      inputExDefinition.parentEl = 'formContainer';
      cb_global.form = new Y.inputEx.Form(inputExDefinition);
      cb_global.form.form.setAttribute('id', 'cb-oeditor-form');

      moveMarkedFormElementsOutOfFieldset(Y);

      // async request data for the object being edited
      Y.io(cb_global.get_url, {
        method: 'GET',
        timeout : ajaxRpcTimeoutMillis,
        on: {
            success: function(id, o, args) {
              var json = parseJson(o.responseText);

              // check status code
              if (json.status != 200) {
                cbShowMsg(formatServerErrorMessage(json.status, json.message));
                return;
              }

              // check payload
              if (!json.payload) {
                cbShowMsg("Server error; server sent no payload.");
                return
              }

              // push payload into form
              var payload = parseJson(json.payload);
              cb_global.form.setValue(payload);

              // record xsrf token if provided
              if (json.xsrf_token) {
                cb_global.xsrf_token = json.xsrf_token;
              } else {
                cb_global.xsrf_token = null;
              }

              // save lastSavedFormValue
              cb_global.original = payload;
              cb_global.lastSavedFormValue = payload;

              // it is better to set lastSavedFormValue to a cb_global.form.getValue(),
              // but it does not work for rich edit control as it has delayed loading
              // and may not be ready when this line above is executed

              // update ui state
              document.getElementById("formContainer").style.display = "block";
              cbShowMsg(json.message);
              setTimeout(function(){ cbHideMsg(); }, 5000);
            },
            failure : function (x,o) {
                cbShowMsg("Server did not respond. Please reload the page to try again.");
            }
        }
      });
  });
</script>

